(ns intro.visualization
  (:require [scicloj.kind-clerk.api :as kind-clerk]
            [tablecloth.api :as tc]
            [aerial.hanami.common :as hc]
            [aerial.hanami.templates :as ht]
            [scicloj.noj.v1.vis.hanami.templates :as vht]
            [scicloj.noj.v1.vis :as vis]
            [scicloj.noj.v1.stats :as stats]
            [tech.v3.datatype :as dtype]
            [tech.v3.datatype.functional :as fun]
            [scicloj.kindly.v3.api :as kindly]
            [scicloj.kindly.v3.kind :as kind]
            [hiccup.core :as hiccup]
            [clojure2d.color :as color]))

Raw html

(-> "<p>Hello, <i>Noj</i>.</p>"
    vis/raw-html)
(-> [:svg {:height 210
           :width 500}
     [:line {:x1 0
             :y1 0
             :x2 200
             :y2 200
             :style "stroke:rgb(255,0,0);stroke-width:2"}]]
    hiccup/html
    vis/raw-html)

Visualizing datases with Hanami

Noj offers a few convenience functions to make Hanami plotting work smoothly with Tablecloth and Kindly.

(def mtcars
  (-> "data/mtcars.csv"
      (tc/dataset {:key-fn keyword})))
(def iris
  (-> "data/iris.csv"
      (tc/dataset {:key-fn keyword})))
(def random-walk
  (let [n 20]
    (-> {:x (range n)
         :y (->> (repeatedly n #(- (rand) 0.5))
                 (reductions +))}
        tc/dataset)))

A simple plot

We can plot a Tablecloth datasete using a Hanami template:

(-> random-walk
    (vis/hanami-plot ht/point-chart
                     {:MSIZE 200}))

Let us look inside the resulting vega-lite space. We can see the dataset is included as CSV:

(-> random-walk
    (vis/hanami-plot ht/point-chart
                     {:MSIZE 200})
    kind/pprint)
{:encoding
 {:y {:field "y", :type "quantitative"},
  :x {:field "x", :type "quantitative"}},
 :mark {:type "circle", :size 200, :tooltip true},
 :width 400,
 :background "floralwhite",
 :height 300,
 :data
 {:values
  "x,y\n0,-0.39000702940466614\n1,-0.28078830153194767\n2,-0.18583467903466833\n3,-0.2577314731282059\n4,-0.05568965021095229\n5,0.4063180065168299\n6,0.8730106031755561\n7,0.8287196401375647\n8,0.5538425075901422\n9,0.3321782149703274\n10,0.7225877270515254\n11,1.1963059146411112\n12,1.6199951810274895\n13,1.2749814756515754\n14,1.5920234461797944\n15,1.4704584756897248\n16,1.890674581112711\n17,2.0918163699328622\n18,1.6509294208303236\n19,1.7701223947136426\n",
  :format {:type "csv"}}}

Additional Hanami templates

The scicloj.noj.v1.vis.hanami.templates namespace add Hanami templates to Hanami’s own collection.

(-> mtcars
    (vis/hanami-plot vht/boxplot-chart
                     {:X :gear
                      :XTYPE :nominal
                      :Y :mpg}))

Layers

(-> random-walk
    (vis/hanami-layers
     {:TITLE "points and a line"}
     [(vis/hanami-plot nil
                       ht/point-chart
                       {:MSIZE 400})
      (vis/hanami-plot nil
                       ht/line-chart
                       {:MSIZE 4
                        :MCOLOR "brown"})]))

Concatenation

(-> random-walk
    (vis/hanami-vconcat
     {}
     [(vis/hanami-plot nil
                       ht/point-chart
                       {:MSIZE 400
                        :HEIGHT 100
                        :WIDTH 100})
      (vis/hanami-plot nil
                       ht/line-chart
                       {:MSIZE 4
                        :MCOLOR "brown"
                        :HEIGHT 100
                        :WIDTH 100})]))
(-> random-walk
    (vis/hanami-hconcat
     {}
     [(vis/hanami-plot nil
                       ht/point-chart
                       {:MSIZE 400
                        :HEIGHT 100
                        :WIDTH 100})
      (vis/hanami-plot nil
                       ht/line-chart
                       {:MSIZE 4
                        :MCOLOR "brown"
                        :HEIGHT 100
                        :WIDTH 100})]))

Linear regression

(-> mtcars
    (stats/add-predictions :mpg [:wt]
                           {:model-type :smile.regression/ordinary-least-square})
    (vis/hanami-layers {}
                       [(vis/hanami-plot nil
                                         ht/point-chart
                                         {:X :wt
                                          :Y :mpg
                                          :MSIZE 200
                                          :HEIGHT 200
                                          :WIDTH 200})
                        (vis/hanami-plot nil
                                         ht/line-chart
                                         {:X :wt
                                          :Y :mpg-prediction
                                          :MSIZE 5
                                          :MCOLOR "purple"
                                          :YTITLE :mpg})]))

Histogram

(-> iris
    (vis/hanami-histogram :sepal-width
                          {:nbins 10}))

Combining a few things together

The following is inspired by the example at Plotnine’s main page. Note how we add regression lines here.

(let [pallete (->> :accent
                   color/palette
                   (mapv color/format-hex))]
  (-> mtcars
      (tc/group-by :gear {:result-type :as-map})
      (->> (sort-by key)
           (map-indexed
            (fn [i [group-name ds]]
              (-> ds
                  (stats/add-predictions :mpg [:wt]
                                         {:model-type :smile.regression/ordinary-least-square})
                  (vis/hanami-layers {:TITLE (str "grear=" group-name)}
                                     [(vis/hanami-plot nil
                                                       ht/point-chart
                                                       {:X :wt
                                                        :Y :mpg
                                                        :MSIZE 200
                                                        :MCOLOR (pallete i)
                                                        :HEIGHT 200
                                                        :WIDTH 200})
                                      (vis/hanami-plot nil
                                                       ht/line-chart
                                                       {:X :wt
                                                        :Y :mpg-prediction
                                                        :MSIZE 5
                                                        :MCOLOR (pallete i)
                                                        :YTITLE :mpg})]
                                     ))))
           (vis/hanami-hconcat nil {}))))
(let [pallete (->> :accent
                   color/palette
                   (mapv color/format-hex))]
  (-> iris
      (tc/group-by :species {:result-type :as-map})
      (->> (sort-by key)
           (map-indexed
            (fn [i [group-name ds]]
              (-> ds
                  (vis/hanami-histogram :sepal-width
                                        {:nbins 10}))))
           (vis/hanami-vconcat nil {}))))
:bye
:bye